第三十三章:流量控制: for 循环

在关于流控制的最后一章中,我们将介绍shell的另一个循环构造。for循环与while和until循环的不同之处在于,它提供了一种在循环过程中处理序列的方法。这在编程时非常有用。因此, for 循环是bash脚本中一种流行的构造。

很自然地,通过 for 复合命令实现了for循环。在bash中, for 有两种形式。

第三十三章:流量控制: for 循环for :传统shell形式为什么是i?for :C语言形式总结

for :传统shell形式

for 命令的原始语法如下:

for variable [ in words]; do commands done

其中 variable 是在循环执行过程中递增的变量的名称, words 是将按顺序分配给变量的可选项目列表, commands 是在循环的每次迭代中执行的命令。

for 命令在命令行上很有用。我们可以很容易地演示它是如何工作的。

在这个例子中, for 给出了一个四个单词的列表:A、B、C和D。对于一个四单词的列表,循环执行了四次。每次执行循环时,都会为变量 i 分配一个单词。在循环中,我们有一个 echo 命令,显示i的值以显示分配。与 whileuntil 循环一样, done 关键字关闭了循环。

for 真正强大的功能是我们可以创建单词列表的许多有趣的方法。例如,我们可以通过大括弧(brace)展开来实现,如下所示:

或者我们可以使用路径名扩展,如下所示:

路径名扩展提供了一个可以在循环中处理的良好、干净的路径名列表。需要采取的一个预防措施是检查扩展是否与某些东西相匹配。默认情况下,如果扩展名与任何文件都不匹配,则将返回通配符本身(上例中的 distors*.txt)。为了防止这种情况,我们将用以下方式在脚本中编写上述示例:

通过添加文件存在性测试,我们将忽略失败的扩展。

另一种常见的单词生成方法是命令替换。

在这个例子中,我们查找文件中找到的最长字符串。当在命令行上给定一个或多个文件名时,此程序使用 strings 程序(包含在GNU binutils包中)在每个文件中生成可读文本“单词”列表。 for 循环依次处理每个单词,并确定当前单词是否是迄今为止找到的最长单词。当循环结束时,将显示最长的单词。

这里要注意的一点是,与我们的惯例相反,我们没有用双引号括住命令替换 $(strings "$1") 。这是因为我们实际上希望通过分词来给出我们的列表。如果我们用引号括住命令替换,它只会产生一个包含文件中每个字符串的单词。这并不是我们想要的。

如果省略 for 命令的 words 选项部分, for 默认处理位置参数。我们将修改 longest-word 脚本以使用此方法:

正如我们所看到的,我们已经改变了最外层的循环,用 for 代替 while 。通过省略 for 命令中的单词列表,将使用位置参数。在循环中,变量i的先前实例已被更改为变量 jshift 的使用也被消除了。

为什么是i?

您可能已经注意到,在前面的每个 for 循环示例中都选择了变量 i 。为什么?除了传统之外,实际上没有具体的原因。与 for 一起使用的变量可以是任何有效的变量,但 i 是最常见的,其次是 jk

这一传统的基础来自Fortran编程语言。在Fortran中,以字母I、J、K、L和M开头的未声明变量会自动键入为整数,而以任何其他字母开头的变量会键入为实数(带小数分数的数字)。这种行为导致程序员将变量I、J和K用于循环变量,因为在需要临时变量(通常是循环变量)时使用它们的工作量更少。

这也导致了以下基于Fortran的俏皮话:

“上帝是真实的,除非声明为整数。”

“GOD is real, unless declared integer.”

for :C语言形式

最近版本的bash添加了第二种 for 命令语法形式,类似于C编程语言中的形式。许多其他语言也支持这种形式。

for (( expression1; expression2; expression3 )); do commands done

这里 expression1expression2expression3 是算术表达式, commands 是在循环的每次迭代期间要执行的命令。

在行为方面,这种形式相当于以下构造:

(( expression1 )) while (( expression2 )); do commands (( expression3 )) done

expression1 用于初始化循环的条件, expression2 用于确定循环何时完成, expression3 在循环的每次迭代结束时执行。

以下是一个典型的应用程序:

执行时,它会产生以下输出:

在这个例子中, expression1 用零值初始化变量 iexpression2 允许循环继续,只要 i 的值小于5, expression3 每次重复循环时都会将 i 的值递增1。

for 的C语言形式在需要数字序列时非常有用。我们将在接下来的两章中看到几个应用程序。

总结

根据我们对 for 命令的了解,我们现在将对 sys_info_page 脚本进行最后的改进。目前, report_home_space 函数看起来像这样:

接下来,我们将重写它,为每个用户的主目录提供更多详细信息,并包括每个目录中的文件和子目录的总数。

这次重写应用了我们迄今为止学到的大部分知识。我们仍然在测试超级用户,但我们没有在 if 中执行完整的操作集,而是在 for 循环中设置了一些稍后使用的变量。我们在函数中添加了几个局部变量,并使用 printf 格式化了一些输出。